/* 模块初始化、卸载的接口头文件  */
#include <linux/module.h>

/* 字符设备头文件 */
#include <linux/cdev.h>
#include <linux/fs.h>

/* 定义主设备号 */
#define MY_MAJOR_NUM	202

/* 定义设备的私有结构体，因为简单，直接使用struct cdev，
如果要支持同个相同的设备，则一般需要为每个设备分配一个结构体，
代表对应的设备 */
static struct cdev aml_cdev;

/* 应用层系统调用：open()、fopen 打开设备节点文件时，将回调此函数  */
static int aml_cdev_open(struct inode *inode, struct file *file)
{
	pr_info("aml_cdev_open() is called.\n");
	return 0;
}

/* 应用层系统调用：close()、fclose 关闭设备节点文件时，将回调此函数  */
static int aml_cdev_close(struct inode *inode, struct file *file)
{
	pr_info("aml_cdev_close() is called.\n");
	return 0;
}

/*  应用层系统调用：ioctrl() 操作设备节点文件时，将回调此函数  */
static long aml_cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	pr_info("aml_cdev_ioctl() is called. cmd = %d, arg = %ld\n", cmd, arg);
	return 0;
}

/* 注册回调函数：只需要实现需要的接口即可 */
static const struct file_operations aml_cdev_fops = {
	.owner = THIS_MODULE,
	.open = aml_cdev_open,
	.release = aml_cdev_close,
	.unlocked_ioctl = aml_cdev_ioctl,
};

/* 模块初始化函数  */
static int __init aml_cdev_init(void)
{
	int ret;
	/* 手动分配一个主设备号、次设备号，需要避开系统已使用的号码*/
	dev_t dev = MKDEV(MY_MAJOR_NUM, 0);
	pr_info("aml_cdev_init \n");

	/* 注册并获取此设备号 */
	ret = register_chrdev_region(dev, 1, "aml_char_device");
	if (ret < 0){
		pr_info("Unable to allocate mayor number %d\n", MY_MAJOR_NUM);
		return ret;
	}

	/* 初始化aml_cdev，并将其添加内核中*/
	cdev_init(&aml_cdev, &aml_cdev_fops);
	ret= cdev_add(&aml_cdev, dev, 1);
	if (ret < 0){
		unregister_chrdev_region(dev, 1);
		pr_info("Unable to add cdev\n");
		return ret;
	}

	return 0;
}

/* 模块退出时执行卸载操作  */
static void __exit aml_cdev_exit(void)
{
	pr_info("aml_cdev_exit\n");
	cdev_del(&aml_cdev);//删除设备
	unregister_chrdev_region(MKDEV(MY_MAJOR_NUM, 0), 1); //注销设备号
}


/* 固定的模板部分，将导出模块的初始化和卸载接口符号 */
module_init(aml_cdev_init);
module_exit(aml_cdev_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("szhou <66176468@qq.com>");
MODULE_DESCRIPTION("This is a amlogic debug tool.");
